package ;
import Files;
import HLex;
import HLlr;
import Macro;
import Scope;
import Subs;
import Writer;
import neko.Sys;
import neko.Lib;
import neko.io.File;









using StringTools;

class MFile {
    public var packages:String;
    public var rname:String;
    public var fname:String;
    public var scope:Scope;
    public var imports:Array<MFile>;
    
    public var importname:String;
    
    public function new(packages:String,fname:String,rname:String,scope:Scope) {
        this.packages = packages;
        this.fname = fname;
        this.rname = rname;
        this.scope = scope;
        imports = [];
        
        importname = rname.substr(0,rname.length-3);
        importname = (~/[\/\\]+/).replace(importname,'.');
        while(importname.charAt(0)=='.') importname = importname.substr(1);
        while(importname.charAt(importname.length-1)=='.') importname = importname.substr(0,importname.length-1);
    }
}

class Main {
    static public var global:Scope = new Scope([],null);
    
    
    static public var mIndet:Scope = new Scope([],null);
    
    static function main() {
        
        var args = Sys.args();
        if(args.length==0 || (args.length%2)==1) {
            Lib.println("Usage: caxe (cx-dir hx-dir )+");
            Sys.exit(1);
        }

        var filenames = Files.get(args);
        var filedata = [];
        var filetokens = [];
        var fileasts = [];
        var files    = [];
        
        var search_dir = [];
        for(i in 0...(args.length>>1))
            search_dir.push(args[i]);
        
        var errs = false;
        
        
        
        neko.Lib.println(" >> Loading files");
        for(src in filenames) {
            var hxl = File.getContent(src);	
            filedata.push({fname:src, data:hxl});
        }
        neko.Lib.println("  "+Std.string(filenames.length)+" .cx files found.");
        
        
        
        var roman = [':D','I','II','III','IV','V','VI','VII','VIII','IX','X'];
        
        var fcnt = filenames.length;
        var curt = 0; var cper = 10;
        neko.Lib.println(" >> Lexing files");
        neko.Lib.print("  "+roman[cper--]);
        for(f in filedata) {
            var src = f.fname;
            var hxl = f.data;
            var tokens = HLex.lexify(hxl);

            var ptokens = HLlr.stream(tokens);
            for(i in ptokens) {
                if(i.id == Terminal.ERROR.id)
                    i.id = Terminal.NOISE.id;
            }
            filetokens.push({fname:src,ptokens:ptokens});
            
            curt++;
            var proc = 10-Std.int(curt/fcnt*10);
            while(cper>=proc) neko.Lib.print(" "+roman[cper--]);
        }
        while(cper>0) neko.Lib.print(" "+roman[--cper]);
        neko.Lib.println("");
        
        
        
        var fcnt = filenames.length;
        var curt = 0; var cper = 10;
        neko.Lib.println(" >> Parsing files");
        neko.Lib.print("  "+roman[cper--]);
        for(f in filetokens) {
            var src = f.fname;
            var ptokens = f.ptokens;
            var fileast = HLlr.parse(ptokens);
    
            if(fileast==null) {
                neko.Lib.println("...Something went wrong in '"+src+"', oh dear!");
                errs = true;
                continue;
            }
            fileasts.push({fname:src, ast:fileast});
            
            curt++;
            var proc = 10-Std.int(curt/fcnt*10);
            while(cper>=proc) neko.Lib.print(" "+roman[cper--]);
        }
        while(cper>0) neko.Lib.print(" "+roman[--cper]);
        neko.Lib.println("");
        
        
        
        neko.Lib.println(" >> Resolving macro and argument scope");
        for(fileast in fileasts) {
            var mindir = "";
            var mincnt = 1000;
            for(i in search_dir) {
                if(fileast.fname.startsWith(i)) {
                    var rname = fileast.fname.substr(i.length);
                    while(rname.charAt(0)=='\\' || rname.charAt(0)=='/')
                        rname = rname.substr(1);
                        
                    if(rname.length<mincnt) {
                        mincnt = rname.length;
                        mindir = rname;
                    }
                }
            }
            var rname = mindir;
            
            var filescope = new Scope(fileast.ast.scope,null,null,null,fileast.fname);
            files.push(new MFile(fileast.ast.packages, fileast.fname, rname, filescope));
        }
        
        
        
        if(errs) Sys.exit(1);
        
        neko.Lib.println(" >> Resolving imports");

        for(i in files) {
            var scope = i.scope;
            for(x in scope.data) {
                switch(x) {
                    case sImport(x):
                        for(dir in search_dir) {
                            var path = dir+'/'+(~/\./).replace(x,'/')+'.cx';
                            var file = null;
                            for(i in files) {
                                if(i.fname == path) { file = i; break; }
                            }
                            if(file!=null && file!=i) {
                                i.imports.push(file);
                                break;
                            }
                        }
                    case sMImport(x):
                        for(dir in search_dir) {
                            var dirpath = neko.io.Path.directory(dir+'/'+x);
                            if(neko.FileSystem.exists(dirpath) && neko.FileSystem.isDirectory(dirpath)) {
                                var ifiles = Files.get([dirpath,dirpath]);
                                for(k in ifiles) {
                                    var file = null;
                                    for(j in files) {
                                        if(j.fname == k) {
                                            file = j;
                                            break;
                                        }
                                    }
                                    if(file!=null && file!=i)
                                        i.imports.push(file);
                                }
                            }
                        }
                    default:
                }
            }
        }
        
        
        
        neko.Lib.println(" >> Performing macro substitutions");
        
        for(i in files) {
            var scope = i.scope;
            var macros = scope.macros_in_scope();

            scope.data = Subs.subs(scope.data,macros,scope);                        
            neko.Lib.println(Writer.print(i,true));
        }
        
        neko.Lib.println(" >> FINISHED");
    }
}

